/**
 * This file is part of Megasquirt Logger for PalmOS.
 * Copyright (C) 2011 Andrew Beard
 *
 * Megasquirt Logger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Megasquirt Logger is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Megasquirt Logger.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "MegasquirtInterface.h"
#include <SerialMgr.h>
#include <MemoryMgr.h>

MegasquirtInterface::MegasquirtInterface( UInt16 nPortID,
                                          UInt16 nRTU_Length,
										  UInt16 nRTU_Delay,
                                          Char   realtimeUpdateCommand ) :
    m_LastError( errNone ),
    m_eInterfaceState( CONNECTED ),
    m_nSerialPortID( nPortID ),
    m_nRealtimeUpdateLength( nRTU_Length ),
    m_bSignatureReceived( false ),
    m_bRealtimeUpdateEvent( false ),
    m_bSignatureEvent( false ),
    m_bDataReady( false ),
    m_SignatureResponseTimeout( SIGNATURE_RESPONSE_TIMEOUT ),
    m_RealtimeUpdateCommand( realtimeUpdateCommand ),
	m_RTU_DelayTimer( nRTU_Delay )
{
    m_pRealtimeUpdateData         = new UInt8[ m_nRealtimeUpdateLength ];
    m_Signature[0]                = '\0';
    m_Signature[SIGNATURE_LENGTH] = '\0';

    m_LastError = SrmSetWakeupHandler( m_nSerialPortID,
                                       RequestedDataReadyCallback,
                                       (UInt32)&m_bDataReady );
	if ( nRTU_Delay == 0 )
	{
	    m_bRTU_DelayEnabled = false;
	}
	else
	{
	    m_bRTU_DelayEnabled = true;
	}
}

MegasquirtInterface::~MegasquirtInterface()
{
    if ( m_pRealtimeUpdateData )
    {
        delete[] m_pRealtimeUpdateData;
        m_pRealtimeUpdateData = NULL;
    }

    m_LastError = SrmSetWakeupHandler( m_nSerialPortID,
                                       NULL,
                                       0 );
    SrmSetReceiveBuffer( m_nSerialPortID,
                         NULL,
                         0 );
}

void MegasquirtInterface::ProcessRxData( void )
{
    UInt32 nBytesWaiting  = 0;
    UInt8* pRxDataFifo    = NULL;

    if ( m_bDataReady )
    {
        switch ( m_eInterfaceState )
        {
            case CONNECTED:
            {
                // We don't need to worry about any incomming data
                break;
            }
            case WAITING_FOR_SIGNATURE:
            {
                m_LastError = SrmReceiveWindowOpen( m_nSerialPortID,
                                                    &pRxDataFifo,
                                                    &nBytesWaiting );
                if ( m_LastError != errNone )
                {
                    // Bad things happened
                    return;
                }

                if ( nBytesWaiting >= SIGNATURE_LENGTH )
                {
                    // Copy data out of the FIFO
                    for ( UInt16 i = 0; i < SIGNATURE_LENGTH; i++ )
                    {
                        m_Signature[i] = pRxDataFifo[i];
                    }
                    m_bSignatureEvent    = true;
                    m_bSignatureReceived = true;
                    m_eInterfaceState    = CONNECTED;
                    m_SignatureResponseTimeout.StopTimer();

                    // Flush all the data in the FIFO
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
                                                         nBytesWaiting );
                }
                else
                {
                    // Not enough data, this shouldn't happen
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
                                                         0 );
                }

                break;
            }
            case WAITING_FOR_REALTIME_UPDATE:
            {
                m_LastError = SrmReceiveWindowOpen( m_nSerialPortID,
                                                    &pRxDataFifo,
                                                    &nBytesWaiting );
                if ( m_LastError != errNone )
                {
                    // Bad things happened
                    return;
                }

                if ( nBytesWaiting >= m_nRealtimeUpdateLength )
                {
                    // Copy data out of the FIFO
                    for ( UInt16 i = 0; i < m_nRealtimeUpdateLength; i++ )
                    {
                        m_pRealtimeUpdateData[i] = pRxDataFifo[i];
                    }
                    m_bRealtimeUpdateEvent = true;
                    m_eInterfaceState      = CONNECTED;

                    // Flush all the data in the FIFO
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
                                                         nBytesWaiting );
                }
                else
                {
                    // Not enough data, this shouldn't happen
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
                                                         0 );
                }
                break;
            }
            default:
            {
                // How did we get here?
                m_eInterfaceState = CONNECTED;
                break;
            }
        }
        m_bDataReady = false;
    }
}

void MegasquirtInterface::RequestSignature( void )
{
    if ( m_eInterfaceState == CONNECTED )
    {
		UInt32 nBytesSent = 0;

		m_eInterfaceState = WAITING_FOR_SIGNATURE;

		m_LastError = SrmReceiveFlush( m_nSerialPortID,
									   0 );

		m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
											 SIGNATURE_LENGTH );

		nBytesSent = SrmSend( m_nSerialPortID,
							  &SIGNATURE_COMMAND,
							  sizeof(SIGNATURE_COMMAND),
							  &m_LastError );

		m_SignatureResponseTimeout.StartTimer();
    }
    else
    {
        // FIXME: Throw an error or something
    }
}

void MegasquirtInterface::RequestRealtimeUpdate( void )
{
    if ( m_eInterfaceState == CONNECTED )
    {
		if ( m_bRTU_DelayEnabled )
		{
		    m_eInterfaceState = DELAYING_REALTIME_UPDATE;
			m_RTU_DelayTimer.StartTimer();
		}
		else
		{
			UInt32 nBytesSent = 0;

			m_eInterfaceState = WAITING_FOR_REALTIME_UPDATE;

			m_LastError = SrmReceiveFlush( m_nSerialPortID,
										   0 );
			m_LastError = SrmSetReceiveBuffer( m_nSerialPortID,
											   m_RxBuffer,
											   RX_BUFFER_SIZE );
			m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
												 m_nRealtimeUpdateLength );

			nBytesSent = SrmSend( m_nSerialPortID,
								  &m_RealtimeUpdateCommand,
								  sizeof(m_RealtimeUpdateCommand),
								  &m_LastError );
		}
    }
    else
    {
        // FIXME: Throw an error or something
    }
}

/**
 * Returns wheather or not we've received a MegaSquirt signture.
 *
 * @return true if we've received a signature from the MegaSquirt, false
 *           otherwise.
 */
bool MegasquirtInterface::ReceivedSignature( void ) const
{
    return m_bSignatureReceived;
}

/**
 * Get the signature of the connected MegaSquirt.
 *
 * @return The data buffer holding the last MegaSquirt signature received.
 */
const Char* MegasquirtInterface::GetSignature( void ) const
{
    return m_Signature;
}

/**
 * Get the payload from the last realtime data update.
 *
 * @return The data buffer holding the last realtime data we received.
 */
const UInt8* MegasquirtInterface::GetRealtimeUpdateData( void ) const
{
    return m_pRealtimeUpdateData;
}

/**
 * Check if we're received a signature response, and clear the event if we
 *   have.
 *
 * @return true if we've received a signature, false otherwise.
 */
bool MegasquirtInterface::CheckSignatureEvent( void )
{
    if ( m_bSignatureEvent )
    {
        m_bSignatureEvent = false;
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * Check if we're received a realtime data update, and clear the event if we
 *   have.
 *
 * @return true if we've received a realtime update, false otherwise.
 */
bool MegasquirtInterface::CheckRealtimeUpdateEvent( void )
{
    if ( m_bRealtimeUpdateEvent )
    {
        m_bRealtimeUpdateEvent = false;
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * Handle a notification that the requested data is available.
 */
void MegasquirtInterface::RequestedDataReadyCallback( UInt32 refCon )
{
    bool* pDataReady = (bool*)refCon;

    *pDataReady = true;
    EvtWakeup();
}

/**
 * Send a new signature request if we're still waiting for a response.
 */
void MegasquirtInterface::ResendSignatureRequest( void )
{
    if ( m_eInterfaceState == WAITING_FOR_SIGNATURE )
    {
        UInt32 nBytesSent = 0;

        m_LastError = SrmReceiveFlush( m_nSerialPortID,
                                       0 );

        m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
                                             SIGNATURE_LENGTH );

        nBytesSent = SrmSend( m_nSerialPortID,
                              &SIGNATURE_COMMAND,
                              sizeof(SIGNATURE_COMMAND),
                              &m_LastError );

        m_SignatureResponseTimeout.StartTimer();
    }
}

/**
 * Check all our timing-based and periodic flags.
 */
void MegasquirtInterface::HandleNilEvent( void )
{
    // Check if we have data first, before checking the timeouts
    ProcessRxData();

    if      ( m_eInterfaceState == WAITING_FOR_SIGNATURE )
    {
        m_SignatureResponseTimeout.CheckTimer();

        if ( m_SignatureResponseTimeout.IsExpired() )
        {
            ResendSignatureRequest();
        }
    }
	else if ( m_eInterfaceState == DELAYING_REALTIME_UPDATE )
	{
	    m_RTU_DelayTimer.CheckTimer();
		
		if ( m_RTU_DelayTimer.IsExpired() )
		{
			UInt32 nBytesSent = 0;

			m_eInterfaceState = WAITING_FOR_REALTIME_UPDATE;

			m_LastError = SrmReceiveFlush( m_nSerialPortID,
										   0 );
			m_LastError = SrmSetReceiveBuffer( m_nSerialPortID,
											   m_RxBuffer,
											   RX_BUFFER_SIZE );
			m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
												 m_nRealtimeUpdateLength );

			nBytesSent = SrmSend( m_nSerialPortID,
								  &m_RealtimeUpdateCommand,
								  sizeof(m_RealtimeUpdateCommand),
								  &m_LastError );		
		}
	}
}